﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using TelnetRouterApplication.View;
using TelnetRouterApplication.Model;

namespace TelnetRouterApplication.Control
{
    public class RouterLevelHandler
    {
        public enum Location :int { 
            //TODO verificar o confiline - configif
            desconnected = 0, connected=1, enabled=2, config=3, configif=4, configline=5, unknown=int.MaxValue
        }

        private static bool _DEBUG = false;
        public static bool DEBUG
        {
            get { return RouterLevelHandler._DEBUG; }
            set { RouterLevelHandler._DEBUG = value; }
        }

        private static RouterLevelHandler instance;
        private RouterLevelHandler() { }

        public static RouterLevelHandler GetInstance()
        {
            if (instance == null)
            {
                instance = new RouterLevelHandler();
            }
            return instance;
        }

        private Location actualLocation;

        public Location ActualLocation
        {
            get { return actualLocation; }
            set { actualLocation = value; }
        }

        public void setInitialState()
        {
            //Envio de vários \r\n para garantir a mensagem contendo a posição
            ConnectionHandler.GetInstance().SendMessage("\r\n\r\n\r\n");

            List<string> message = ConnectionHandler.GetInstance().ReceiveMessage();
            if (message.Count <= 0)
            {
                //TODO verificar se modifico para desconnected
                ActualLocation = Location.unknown;
                return;
            }
            string position = message[message.Count-1];
            //Vai representar o indicador da posição
            string locale = "";
            
            //Se possuir parêntesis na posição, então preciso pegar do parêntesis até o #
            if (position.IndexOf('(') > -1 && position.IndexOf('#') > -1) 
            {
                if (DEBUG)
                {
                    System.Diagnostics.Debug.WriteLine("Position> " + position + " possui '(' na posição " + position.IndexOf('('));
                }
                if (position.IndexOf('#') < position.IndexOf('('))
                {
                    position = position.Substring(position.IndexOf('#')+1);
                }
                int lenght = position.IndexOf('#') - position.IndexOf('(') + 1;

                locale = position.Substring(position.IndexOf('('), lenght);

                //Descobrir qual é a localização
                switch (locale)
                {
                    case "(config)#": actualLocation = Location.config; break;
                    case "(config-if)#": actualLocation = Location.configif; break;
                    case "(config-line)#": actualLocation = Location.configline; break;
                    default: actualLocation = Location.unknown; break;
                }
            }
            else if (position.IndexOf('#') > -1)
            {
                locale = position.Substring(position.IndexOf('#'));

                //Nesse caso, estamos no enable

                actualLocation = Location.enabled;
            }
            else if (position.IndexOf('>') > -1)
            {
                locale = position.Substring(position.IndexOf('>'));

                //Nesse caso estamos apenas conectados

                actualLocation = Location.connected;
            }
            else
            {
                //Nesse caso, aparentemente, estamos desconectados do servidor

                actualLocation = Location.desconnected;
                if (DEBUG)
                {
                    System.Diagnostics.Debug.WriteLine("Reconexão!");
                }
                ConnectionHandler.GetInstance().Connect();
                ConnectionHandler.GetInstance().SendMessage("\r\n\r\n\r\n");
                ConnectionHandler.GetInstance().ReceiveMessage();
            }
            

            if (DEBUG)
            {
                System.Diagnostics.Debug.WriteLine("Position> " + position);
                System.Diagnostics.Debug.WriteLine("Locale> " + locale);
            }
        }

        public bool GoTo(Location destination, string especification)
        {
            //Método para ir até determinado local partindo da location
            //Como esse método é usado pela aplicação, então nunca acontecerá dela ser chamada para 
            //um destino desconhecido, embora ela possa descobrir que está em uma posição desconecida

            //Primeiro, devemos rever onde estamos
            if (destination == Location.desconnected)
            {
                return false;
            }

            setInitialState();
            //Caso inicial, o programa está em um nível mais abaixo do desejado e deve subir até lá
            if (actualLocation < destination)
            {
                switch (actualLocation)
                {
                    case Location.connected:
                        GoToEnable();
                        actualLocation = Location.enabled;
                        goto case Location.enabled;
                    case Location.enabled:
                        if (actualLocation == destination)
                        {
                            break;
                        }
                        GoToConfig();
                        actualLocation = Location.config;
                        goto case Location.config;
                    case Location.config:
                        if (actualLocation == destination)
                        {
                            break;
                        }
                        if (especification.Contains("interface"))
                        {
                            ConnectionHandler.GetInstance().SendMessage(especification);
                            actualLocation = Location.configif;
                            goto case Location.configif;
                        }
                        else if (especification.Contains("line"))
                        {
                            ConnectionHandler.GetInstance().SendMessage(especification);
                            actualLocation = Location.configline;
                            goto case Location.configline;
                        }
                        else
                        {
                            //Se não foi passado especificação nenhuma, esse destino é desconhecido
                            return false;
                        }
                    case Location.configif:
                        if (actualLocation == destination)
                        {
                            break;
                        }
                        goto case Location.unknown;
                    case Location.configline:
                        if (actualLocation == destination)
                        {
                            break;
                        }
                        goto case Location.unknown;

                    case Location.unknown: 
                        ConnectionHandler.GetInstance().SendMessage("exit");
                        return GoTo(destination, especification);
                }

                if (actualLocation != destination)
                {
                    return GoTo(destination, especification);
                }
                else
                {
                    return true;
                }
            }
            else if (actualLocation > destination)
            {
                if (actualLocation == Location.enabled)
                {
                    if (DEBUG)
                    {
                        System.Diagnostics.Debug.WriteLine("(Disable) Actual Location> " + actualLocation);
                    }
                    ConnectionHandler.GetInstance().SendMessage("disable");
                }
                else if(actualLocation != Location.connected)
                {
                    if (DEBUG)
                    {
                        System.Diagnostics.Debug.WriteLine("(Exit) Actual Location> " + actualLocation);
                    }
                    ConnectionHandler.GetInstance().SendMessage("exit");
                }
                return GoTo(destination, especification);
            }
            else
            {
                //Caso sejam iguais, já retorna verdadeiro
                return true;
            }

        }

        public bool GoTo(Location destination)
        {
            return GoTo(destination, "");
        }

        public void GoToEnable()
        {
            ConnectionHandler.GetInstance().SendMessage("enable");
            if (ConnectionHandler.GetInstance().NeedPassword())
            {
                bool success = false;
                while (!success)
                {
                    if (ConfigutarionHandler.GetInstance().RouterSettings == null)
                    {
                        ConfigutarionHandler.GetInstance().RouterSettings = new SettingsModel("", "");
                        ConfigutarionHandler.GetInstance().RouterSettings.EnablePassword = ConnectionHandler.Password;
                    }
                    
                    string password = ConfigutarionHandler.GetInstance().RouterSettings.EnablePassword;

                    success = ConnectionHandler.GetInstance().SendPassword(password);
                    if (!success)
                    {
                        InputBoxForm inputBox = new InputBoxForm("Enable password is wrong. Please, retype it");
                        if (DEBUG)
                        {
                            System.Diagnostics.Debug.WriteLine("Enable password is wrong. Please, retype it!");
                        }
                        DialogResult dialog = inputBox.ShowDialog();
                        string newPassword = "";
                        if (dialog == DialogResult.OK)
                        {
                            newPassword = inputBox.MessageWritten;
                        }
                        ConfigutarionHandler.GetInstance().RouterSettings.EnablePassword = newPassword;
                        ConnectionHandler.Password = newPassword;
                    }
                }

            }
        }

        public void GoToConfig()
        {
            ConnectionHandler.GetInstance().SendMessage("config");
            //Ler as mensagens que aparecem quando você entra no config
            ConnectionHandler.GetInstance().ReceiveMessage();
            //Dar o comando para seguir em frente
            ConnectionHandler.GetInstance().SendMessage("");
            //TODO tratar caso possua senha
        }
    }
}
